<template>
    <header class="button-container">
        <el-button id="btn_setting" type="primary" :icon="CreditCard" @click="newGraph">新建</el-button>
        <el-input id="btn_setting" style="width: 120px; height:33px;" v-model="data.fileName" placeholder="输入文件名" />
        <el-button id="btn_setting" type="primary" :icon="CreditCard" @click="saveGraph(false)">保存</el-button>
        <el-button id="btn_setting" type="primary" :icon="CreditCard" @click="releaseGraph">发布</el-button>
        <el-button id="btn_setting" type="primary" :icon="CreditCard" @click="setAttrib">属性</el-button>
        <el-button id="btn_setting" type="primary" :icon="CreditCard" @click="clearCells">清空</el-button>
        <el-button id="btn_setting" type="primary" :icon="Upload" @click="exportGraph">导出</el-button>
        <el-upload ref="uploadRef" class="upload-btn" :auto-upload="false" :on-change="importGraph" accept="*.json"
            :limit="1">
            <el-button id="btn_setting" type="primary" :icon="Download">导入</el-button>
        </el-upload>
        <el-button id="btn_setting" type="primary" :icon="ZoomOut" @click="zoom(-1)"></el-button>
        <el-button id="btn_setting" type="primary" :icon="ZoomIn" @click="zoom(1)"></el-button>
        <el-button id="btn_setting" type="primary" :icon="Search" @click="zoom(0)"></el-button>
        <el-button id="btn_setting" type="primary" @click="autoSN">自动标记</el-button>
    </header>
    <main class="graph-container">
        <div id="graph-container" />
    </main>
    <el-dialog width="750" v-model="data.dlg.show" :title="data.dlg.title">
        <div v-if="data.dlg.dataType == 1" style="margin:0 auto; width:500px;">
            <el-row>
                <el-checkbox v-model="data.isEnabledQulityCheckState" label="启用质量验证" size="large"
                    @change="changeQulityCheckState" />
            </el-row>
            <el-row>
                <span>测点类别:</span>
                <el-input v-model="data.io.category" placeholder="输入测点类别">
                </el-input>
            </el-row>
            <br>
            <el-row>
                <span>测点名称:</span>
                <el-input v-model="data.io.tag" placeholder="输入测点名称">
                </el-input>
            </el-row>
            <br>
            <el-row>
                <span>测点描述:</span>
                <el-input v-model="data.io.desc" placeholder="输入测点描述">
                </el-input>
            </el-row>
        </div>
        <el-tabs v-else style="width=500px;height=400px;" type="border-card">
            <el-tab-pane label="基本属性">
                <el-row>
                    <el-col :span="12">
                        <el-input v-model="data.snValue"> <template #prepend>执行顺序</template> </el-input>
                    </el-col>
                    <el-col :span="12">
                        <el-checkbox v-model="data.isEnabledQulityCheckState" label="启用质量验证" size="large"
                            @change="changeQulityCheckState" />
                    </el-col>
                </el-row>
                <el-row>
                    <el-table ref="singleTableRef" :data="commonTableDatas">
                        <el-table-column type="index" width="40" />
                        <el-table-column property="data.name" label="名称" />
                        <el-table-column property="data.sv" label="设定值">
                            <template #default="scope">
                                <el-input v-model="scope.row.data.sv" />
                            </template>
                        </el-table-column>
                        <el-table-column property="data.dv" label="默认值" />
                        <el-table-column property="data.dataType" label="类型" width="120px" />
                        <el-table-column property="data.desc" label="注释" width="220px" />
                    </el-table>
                </el-row>
            </el-tab-pane>
            <el-tab-pane label="强制面板">强制面板</el-tab-pane>
            <el-tab-pane label="趋势图">趋势图</el-tab-pane>
            <el-tab-pane label="算法说明">算法说明</el-tab-pane>
            <el-tab-pane label="注释">注释</el-tab-pane>
        </el-tabs>
        <template #footer>
            <span class="dialog-footer">
                <el-button type="primary" @click="onDlgSummit"> 应用 </el-button>
                <el-button @click="data.dlg.show = false">关闭</el-button>
            </span>
        </template>
    </el-dialog>
</template>

<script setup  lang="ts">
import { onMounted, ref, reactive, computed } from "vue";
import { CreditCard, Download, Upload, ZoomOut, ZoomIn, Search } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'

import { Cell, Edge, Graph, Node, Shape } from '@antv/x6'
import { Transform } from '@antv/x6-plugin-transform'
import { Selection } from '@antv/x6-plugin-selection'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Clipboard } from '@antv/x6-plugin-clipboard'
import { History } from '@antv/x6-plugin-history'

import { easyapi, TID } from '@/lib/lib.ts'

interface Data {
    [property: string]: any
}

let graph: Graph;
const uploadRef = ref();


const data: Data = reactive({
    // curNode: "",//当前选择的节点
    dlg: { //节点对话框
        node: {},//选择的节点
        title: "属性",//标题
        show: false, //是否显示
        dataType: 0,//对话框类型
    },
    io: {
        tag: "", //标签
        desc: "",//描述
        category: "",//类别
    },
    fileName: "",//画布文件名
    zoom: 1,//放大级别
    attrib: null,
    //zIndex: 0, //上一次的z-index
    width: 0, //画布宽度，暂未使用
    height: 0,//画布高度，暂未使用
    modified: false, //画布是否已经修改
    snValue: "",//SN值
    isEnabledQulityCheckState: false //禁用质量验证
})

//基础属性表格数据
const commonTableDatas = computed(() => {
    //   let node:any =  graph.getCellById(data.dlg.nodeid)

    return data.dlg.node.ports.items
})

/**
 * 质量验证状态变更事件
 * @param value 
 */
const changeQulityCheckState = (value: boolean) => {
    let nodeData = data.dlg.node.getData()
    nodeData.isEnabled = value
    data.dlg.node.setData(nodeData)
}

//提交对话框
const onDlgSummit = () => {
    const nodeLabel = data.dlg.node.getLabel()
    const sn = data.snValue
    data.dlg.node.data.sn = Number(sn)
    if (sn) {
        data.dlg.node.setLabel(`${nodeLabel}-${sn}`)
    }
    if (data.dlg.dataType == 0) { //普通算子
        //转换SV的值
        (data.dlg.node.ports.items as any[]).forEach(item => {
            item.data.sv = item.data.sv ? Number(item.data.sv) : item.data.sv
        });
        const node = graph.getCellById(data.dlg.node.id) as Node
        const nodePorts = node.getPorts()
        nodePorts.forEach(port => {
            let text = port.data.sv ? `${port.data.name}:${port.data.sv}` : port.data.name
            //TODO.官网BUG。直接更新值检测不到数据变化。
            node.portProp(port.id!, "attrs/text/text", TID())
            node.portProp(port.id!, "attrs/text/text", text)
        })
    } else {//其他算子
        let text: string = ""
        if (data.io.tag) {
            text = data.io.tag;
        }
        if (data.io.desc) {
            if (text != "") {
                text = text + ":" + data.io.desc
            } else {
                text = data.io.desc
            }
        }
        if (sn) {
            if (text == "") {
                text = data.dlg.data.sysmbol
            }
            text = text + "-" + sn;
            data.dlg.node.setProp({ attrs: { text: { text: text } } })
        } else {
            if (text == "") {
                text = data.dlg.data.sysmbol
            }
            data.dlg.node.setProp({ attrs: { text: { text } } })
        }
        data.dlg.node.setProp({
            data: {
                extends: {
                    tag: data.io.tag,
                    desc: data.io.desc,
                    category: data.io.category,
                }
            }
        })
        //  data.dlg.node.setProp({ data: { extends: { desc: data.io.desc } } })
        data.io.desc = "";
        data.io.tag = "";
        data.io.category = "";
    }
    data.dlg.show = false;
}

//新建一张画布
const newGraph = () => {
    //如果文件已经更新，提示保存文件
    if (data.modified) {
        ElMessageBox.confirm(
            '画布已经修改，是否保存?',
            'Warning',
            {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            }
        ).then(() => {
            saveGraph(false) //保存
            graph.clearCells();
            data.fileName = "";
        }).catch(() => {
            graph.clearCells();
            data.fileName = "";
        })
        return;
    }
    graph.clearCells();
    data.fileName = "";
}

/**
 * @description: 发布图形
 * @return {*}
 */
const releaseGraph = () => {
    ElMessageBox.confirm(
        '图形发布后不能撤回，是否继续?',
        'Warning',
        {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
        }
    ).then(() => {
        saveGraph(true);
    })
}

/**
 * @description: 设置图形的属性
 * @return {*}
 */
const setAttrib = () => {
    ElMessageBox.prompt('请输入画布属性', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        //message:data.attrib,
        inputValue:data.attrib,
        inputType:"textarea",
    }).then(({ value }) => {
        if (value) {
            data.attrib = value;
        }
    })
}

//设置当前选择的node
// const setCurNode = (jsonNode: string) => {
//     debugger
//     data.curNode = jsonNode;
// }

//放大或缩小，正数放大，负数缩小
const zoom = (k: number) => {
    let zoom = graph.zoom()
    if (k > 0) {
        if (zoom < 1.5) {
            graph.zoom(0.1)
        }
    }
    if (k < 0) {
        if (zoom > 0.5) {
            graph.zoom(-0.1)
        }
    }
    if (k == 0) {
        graph.zoomTo(1)
    }
    data.zoom = graph.zoom();
}



/**
 * @description: 自动标记执行顺序
 * @return {*}
 */
const autoSN = () => {
    let cells: Cell[] = graph.getCells();

    let ns: any = [];//node 集合
    let edges: Edge[] = [];//边的集合
    let outs: any = [];//结果的集合
    let objNode: any = {};
    let objEdge: any = {};

    cells.forEach((cell: Cell) => {
        if (cell.isEdge()) {
            edges.push(cell);
            objEdge[cell.id] = cell;
        } else if (cell.isNode()) {
            if (cell.shape == "rect") {
                return;
            }
            if (!cell.data) {
                return;
            }
            // console.log(cell.label, cell.shape)
            ns.push(cell);
            objNode[cell.id] = cell;
            //console.log(cell.id, cell.label, cell.data.modelId)
            if (cell.data.modelId == "2") {
                outs.push(cell);
            }
        }
    })

    edges.forEach((edge: Edge) => {
        let x: any = edge.getTarget();
        if (x.port) {
            objEdge[x.port] = edge;
        }
    })


    let idx: number = 0;
    let cnt: number = 0;
    let ok: boolean = true;

    let fEdge = (id: string, edge: any) => {
        cnt++;
        if (cnt > 500) { //最多支持1000条线寻址
            ok = false;
            return;
        }
        let src: any = edge.getSource();
        let node: any = objNode[src.cell];
        console.log(id, "==>", src.cell, src.port, node.id, node.label)
        fNode(node)
    }
    let fNode = (node: any) => {
        console.log(node.id)
        if (!ok) {
            return;
        }
        if (!node) {
            return;
        }
        console.log(node.id)
        idx++
        node.data.sn1 = idx;
        //console.log(node);
        let ports: any[] = node.getPorts();
        for (let i = ports.length - 1; i >= 0; i--) {
            let port: any = ports[i];
            // ports.forEach((port: any) => {
            // console.log(node.label, node.id, port.id, port.data.kind)
            if (port.data.kind == "in") {//目的端口
                console.log(node.label, node.id, port.id, port.data.kind, port.attrs.text.text);//找到连接桩的id
                let edge = objEdge[port.id];
                if (edge) {
                    fEdge(port.id, edge)
                }
            }
        }
    }

    outs.forEach((out: any) => {
        if (!ok) {
            return;
        }
        fNode(out);
    })

    if (!ok) {
        ElMessage("自动标记失败, 请人工标记");
        return
    }

    ns.sort((a: any, b: any) => {
        if (a.data.sn1 != null && b.data.sn1 != null) {
            if (a.data.sn1 > b.data.sn1) {
                return -1;
            } else {
                return 1;
            }
        }
        return true;
    })

    ns.forEach((node: any, index: number) => {
        if (node.data.sn1 != null) {
            node.data.sn = index + 1;// idx - node.data.sn1;//
            node.data.sn1 = null;

            if (node.data.modelId && node.data.modelId == "1" || node.data.modelId == "2") {
                let text = "";//port.data.sv ? `${port.data.name}:${port.data.sv}` : port.data.name
                if (node.data.extends?.tag) {
                    text = node.data.extends.tag
                }
                if (node.data.extends?.desc) {
                    if (text == "") {
                        text = node.data.extends.desc
                    } else {
                        text = text + ":" + node.data.extends.desc
                    }
                }
                if (text == "") {
                    node.label = node.data.sysmbol + "-" + node.data.sn;
                } else {
                    node.label = text + "-" + node.data.sn;
                }
            } else {
                node.label = node.data.sysmbol + "-" + node.data.sn;
            }

            console.log(node.label, node.id, node.data.sn);
        }
    })
    //console.log(cells);
}


//保存图形
const saveGraph = (isRelease: boolean) => {
    let obj: any = graph.toJSON();
    obj["filename"] = data.fileName;//设置图形的文件名
    obj["zoom"] = data.zoom; //设置图形的缩放比例
    obj["attrib"] = data.attrib;//设置图形的属性
    localStorage.setItem("lastnode", JSON.stringify(obj));
    if (data.fileName == "") {
        ElMessageBox.prompt('请输入保存的文件名', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
        }).then(({ value }) => {
            if (value) {
                data.fileName = value;
                obj["filename"] = data.fileName;
                if (isRelease) {
                    releaseToServer(obj);
                } else {
                    saveToServer(obj);
                }
            }
        })
    } else {
        if (isRelease) {
            releaseToServer(obj);
        } else {
            saveToServer(obj);
        }
    }
}

//删除画布全部元素
const clearCells = () => {
    ElMessageBox.confirm(
        '是否清除画布全部元素?',
        'Warning',
        {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
        }
    ).then(() => {
        graph.clearCells();
    })
}

//保存到服务器
const saveToServer = (obj: any) => {
    easyapi("/v1/api/save", "post", obj, (resp: any) => {
        data.modified = false;//文件已经保存
        ElMessage(resp.data.msg);
    })
}

//发布到服务器
const releaseToServer = (obj: any) => {
    easyapi("/v1/api/release", "post", obj, (resp: any) => {
        data.modified = false;//文件已经保存
        ElMessage(resp.data.msg);
    })
}

//导入图形
const importGraph = (file: any, _fs: any) => {
    // console.log(file);
    // console.log(fs);
    // data.fileName = file.name.substring(0, file.name.lastIndexOf("."))    
    let reader: any = new FileReader();
    reader.onload = function () {
        if (reader.result) {
            let cells = JSON.parse(reader.result);
            uploadRef.value.clearFiles();
            cells["filename"] = file.name.substring(0, file.name.lastIndexOf("."))
            loadGraph(cells);
        }
    };
    reader.readAsText(file.raw);
}

//加载节点和线
const loadGraph = (obj: any) => {
    //如果文件已经更新，提示保存文件
    if (data.modified) {
        ElMessageBox.confirm(
            '画布已经修改，是否保存?',
            'Warning',
            {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            }
        ).then(() => {
            saveGraph(false) //保存
            loadGraphWithoutCheck(obj)
        }).catch(() => {
            loadGraphWithoutCheck(obj)
        })
        return;
    }
    loadGraphWithoutCheck(obj)
}

//加载图形，不做任何检查
const loadGraphWithoutCheck = (obj: any) => {
    let ns: any = [];//node 集合
    let edges: any = [];
    if (obj.filename) {
        data.fileName = obj.filename;
    } else {
        data.fileName = "";
    }
    if (obj.zoom) {
        data.zoom = obj.zoom;
    } else {
        data.zoom = 1;
    }
    if (obj.attrib) {
        data.attrib = obj.attrib;
    } else {
        data.attrib = null;
    }
    graph.clearCells();

    obj.cells.forEach((item: any) => {
        if (item.shape == "edge") {
            edges.push(item);
        } else {
            ns.push(item);
        }
    })
    graph.addNodes(ns);
    graph.addEdges(edges);
    if (data.zoom) {
        graph.zoomTo(data.zoom);
    }
}

//导出图形
const exportGraph = () => {
    // 要保存的字符串, 需要先将数据转成字符串
    let obj: any = graph.toJSON();
    obj["filename"] = data.fileName;//设置图形的文件名
    obj["zoom"] = data.zoom; //设置图形的缩放比例
    obj["attrib"] = data.attrib;//设置图形的属性

    const stringData = JSON.stringify(obj)
    // dada 表示要转换的字符串数据，type 表示要转换的数据格式
    const blob = new Blob([stringData], {
        type: 'application/json'
    })
    // 根据 blob生成 url链接
    const objectURL = URL.createObjectURL(blob)
    // 创建一个 a 标签Tag
    const aTag = document.createElement('a')
    // 设置文件的下载地址
    aTag.href = objectURL
    // 设置保存后的文件名称
    aTag.download = data.fileName + ".json"
    // 给 a 标签添加点击事件
    aTag.click()
    // 释放一个之前已经存在的、通过调用 URL.createObjectURL() 创建的 URL 对象。
    // 当你结束使用某个 URL 对象之后，应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。
    URL.revokeObjectURL(objectURL)
}

//新增节点
const addNode = (x: any, y: any, node: any) => {
    // let node = JSON.parse(data.curNode)//JSON.stringify(data.nodes[index]));
    node.id = TID();
    node.x = x;
    node.y = y;
    node.ports.forEach((port: any) => {
        port.id = TID();
    })
    // node.data={isEnabled:true}
    if (node.data == null) {
        node.data = {}
    }
    node.data.isEnabled = false
    graph.addNode(node);
    data.modified = true; //文件已经更新过
}

/**
 * 是否允许连接
 * @param edge:连接对象
 */
const isAllowConnectEdgeByDataType = (edge: Edge): boolean => {
    let isAllowConnect = false
    const sourceNode = edge.getSourceNode()
    const sourcePortId = edge.getSourcePortId()
    let sourcePortData

    const targetNode = edge.getTargetNode()
    const targetPortId = edge.getTargetPortId()
    let targetPortData

    if (sourceNode) {
        const sourceNodePortItems = sourceNode.ports
        sourcePortData = sourceNodePortItems.items.find(item => item.id === sourcePortId)
    }
    if (targetNode) {
        const targetNodePortItems = targetNode.ports
        targetPortData = targetNodePortItems.items.find(item => item.id === targetPortId)
    }
    if (sourcePortData && targetPortData && sourcePortData.data.dataType === targetPortData.data.dataType) {
        isAllowConnect = true
    }
    !isAllowConnect && ElMessage.error("类型不一致，禁止连接");
    return isAllowConnect
}

Shape.Rect.config({
    id: TID(),
})

// #region 初始化画布
onMounted(() => {
    // #region 初始化画布
    graph = new Graph({
        container: document.getElementById('graph-container') as HTMLElement,
        grid: {
            size: 5,
            visible: true,
        },
        mousewheel: {
            enabled: true,
            zoomAtMousePosition: true,
            modifiers: 'ctrl',
            minScale: 0.5,
            maxScale: 3,
        },
        panning: {
            enabled: true,
            modifiers: 'ctrl',
        },
        connecting: {
            router: {
                name: 'manhattan',
                args: {
                    startDirections: ['right'],
                    endDirections: ['left'],
                    padding: 15,
                },
            },
            anchor: 'center',
            connectionPoint: 'boundary',
            allowBlank: false,
            allowMulti: 'withPort',//允许多个
            allowLoop: false,
            snap: {
                radius: 20,
            },
            createEdge() {
                data.modified = true; //文件已经更新过
                return new Shape.Edge({
                    id: TID(),
                    attrs: {
                        line: {
                            //stroke: '#1890ff',
                            stroke: '#A2B1C3',
                            strokeWidth: 2,
                            targetMarker: {
                                name: 'block',
                                width: 12,
                                height: 8,
                            },
                        },
                    },
                    zIndex: 0,
                })
            },
            validateConnection({ targetMagnet }) {
                return !!targetMagnet
            },
            validateEdge: ({ edge }) => {
                return isAllowConnectEdgeByDataType(edge)
            }
        },
    })

    //注册算子节点
    Graph.registerNode(
        'op-rect',
        {
            inherit: 'rect',
            width: 55,
            height: 40,
            id: TID(),
            attrs: {
                label: {
                    refY: -10,
                    fontSize: 14,
                },
            },
            ports: {
                groups: {
                    in: {
                        position: 'left',
                        label: {
                            position: {
                                name: 'inside',
                                args: {
                                    attrs: {
                                        text: {
                                            refX: 7,
                                            fontSize: 12,
                                        }
                                    }
                                }
                            }
                        },
                        attrs: {
                            circle: {
                                r: 5,
                                magnet: true,
                                stroke: '#31d0c6',
                                fill: '#fff',
                                strokeWidth: 2,
                            },
                        },
                    },
                    out: {
                        position: 'right',
                        label: {
                            position: {
                                name: 'inside',
                                args: {
                                    attrs: {
                                        text: {
                                            refX: -7,
                                            fontSize: 12,
                                        }
                                    }
                                }
                            }
                        },
                        attrs: {
                            circle: {
                                r: 5,
                                magnet: true,
                                stroke: '#31d0c6',
                                fill: '#fff',
                                strokeWidth: 2,
                            },
                        },
                    },
                },
            },
        },
        true,
    );

    //注册IO算子
    Graph.registerNode(
        'op-rect-io',
        {
            inherit: 'rect',
            width: 100,
            id: TID(),
            ports: {
                groups: {
                    in: {
                        position: 'right',
                        attrs: {
                            circle: {
                                r: 5,
                                magnet: true,
                                stroke: '#31d0c6',
                                fill: '#fff',
                                strokeWidth: 2,
                            },
                        },
                    },
                    out: {
                        position: 'left',
                        attrs: {
                            circle: {
                                r: 5,
                                magnet: true,
                                stroke: '#31d0c6',
                                fill: '#fff',
                                strokeWidth: 2,
                            },
                        },
                    },
                },
            },
        },
        true,
    )

    //加载上一次的图形
    let lastnode = localStorage.getItem("lastnode");
    if (lastnode != null) {
        let cells = JSON.parse(lastnode);
        loadGraph(cells);
    }

    // #region 使用插件
    graph
        .use(
            new Transform({
                resizing: true,
                rotating: true,
            }),
        )
        .use(
            new Selection({
                rubberband: true,
                showNodeSelectionBox: true,
            }),
        )
        .use(new Snapline())
        .use(new Keyboard())
        .use(new Clipboard())
        .use(new History())
    // #endregion

    // #region 快捷键与事件
    // 不能使用复制，复制会产生相同的portid 对后续影响很大
    graph.bindKey(['meta+c', 'ctrl+c'], () => {
        const cells = graph.getSelectedCells()
        graph.copy(cells);
        return false
    })

    graph.bindKey(['meta+x', 'ctrl+x'], () => {
        const cells = graph.getSelectedCells()
        if (cells.length) {
            graph.cut(cells)
        }
        return false
    })

    graph.bindKey(['meta+v', 'ctrl+v'], () => {
        if (!graph.isClipboardEmpty()) {
            //原始粘贴不可用， id重复， 优化后的id可以正常使用
            //粘贴算法
            // 1 重粘贴板中读取元素
            // 2 把元素转化为json对象
            // 3 从json创建新的元素
            // 4 删除粘贴后的元素
            // 5 选择新创建的元素 
            const cells = graph.paste({ offset: 32 })
            graph.cleanSelection()

            const mapCell: any = {}
            const cs: any = [];
            const newCell = [];

            //优化策略， 粘贴后获取对象后删除， 重排id
            cells.forEach((cell: any) => {
                let id = TID();
                mapCell[cell.id] = id;
                let c: any = cell.toJSON();
                console.log(c)
                if (cell.isNode()) {
                    c.id = id;
                    c.ports.items.forEach((port: any) => {
                        let id = TID();
                        mapCell[port.id] = id;
                        port.id = id;
                    })
                    newCell.push(graph.addNode(c))
                } else {
                    cs.push(c)
                }
            })

            cs.forEach(c => {
                let id = mapCell[c.id];
                console.log(id)
                c.id = TID();
                if (c.shape == "edge") {
                    if (c.source.cell) {
                        id = mapCell[c.source.cell];
                        c.source.cell = id
                    }
                    if (c.source.port) {
                        id = mapCell[c.source.port];
                        c.source.port = id
                    }
                    if (c.target.cell) {
                        id = mapCell[c.target.cell];
                        c.target.cell = id
                    }
                    if (c.target.port) {
                        id = mapCell[c.target.port];
                        c.target.port = id
                    }
                    newCell.push(graph.addEdge(c))
                }
            });

            graph.removeCells(cells);//删除已经粘贴的对象 
            graph.select(newCell);//重新选择新的对象
        }
        return false
    })

    // undo redo
    graph.bindKey(['meta+z', 'ctrl+z'], () => {
        if (graph.canUndo()) {
            graph.undo()
        }
        return false
    })

    // 保存到服务器
    graph.bindKey(['ctrl+alt+s'], () => {
        saveGraph(false);
    })
    //重做
    graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
        if (graph.canRedo()) {
            graph.redo()
        }
        return false
    })

    // 选择全部
    graph.bindKey(['meta+a', 'ctrl+a'], () => {
        const ns = graph.getNodes()
        if (ns) {
            graph.select(ns)
        }
    })


    // 删除
    graph.bindKey('delete', () => {
        const cells = graph.getSelectedCells()
        if (cells.length) {
            graph.removeCells(cells)
        }
    })

    // 放大
    graph.bindKey(['ctrl+1', 'meta+1'], () => {
        const zoom = graph.zoom()
        if (zoom < 1.5) {
            graph.zoom(0.1)
        }
    })

    //缩小
    graph.bindKey(['ctrl+2', 'meta+2'], () => {
        const zoom = graph.zoom()
        if (zoom > 0.5) {
            graph.zoom(-0.1)
        }
    })

    //点击空白画布事件，创建一个标签
    graph.on('blank:contextmenu', ({ x, y }) => {
        graph.addNode({
            shape: 'rect',
            x: x,
            y: y,
            width: 150,
            label: '新标签',
            height: 20,
            attrs: {
                rect: {
                    strokeWidth: 0
                },
            }
        })
    })

    //点击连线事件
    graph.on('edge:click', ({ edge }) => {
        if (!edge.hasTool('vertices')) {
            edge.addTools([{ name: 'vertices' }])
        }
    })

    //线条高亮
    graph.on('edge:mouseenter', ({ edge }) => {
        edge.attr('line', { stroke: '#1890ff' });
        //data.zIndex = edge.getZIndex();
        edge.setZIndex(200);
    })

    //线雕恢复正常
    graph.on('edge:mouseleave', ({ edge }) => {
        edge.attr('line', { stroke: '#A2B1C3' });
        edge.setZIndex(0);
        if (edge.hasTool('vertices')) {
            edge.removeTool('vertices')
            document.body.style.cursor = "default"
        }
    })

    //显示节点属性对话框
    graph.on('node:dblclick', ({ node }) => {
        //console.log(node);
        if (node.shape == "rect") {
            //console.log(node);
            ElMessageBox.prompt('请输入标签名称', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                inputValue: node.prop("attrs/text/text"),
            }).then(({ value }) => {
                if (value) {
                    node.prop("attrs/text/text", value);
                }
            })
            return;
        }
        data.snValue = ""
        if (node.data.sn != null) {
            data.snValue = node.data.sn
        }
        data.dlg.node = node;
        data.isEnabledQulityCheckState = data.dlg.node.getData().isEnabled
        if (node.data.sysmbol) {
            data.dlg.title = node.data.sysmbol + "属性";
        } else {
            data.dlg.title = "属性";
        }
        if (node.data.modelId) {
            if (node.data.modelId == "1" || node.data.modelId == "2") {
                data.dlg.dataType = 1;
                data.dlg.show = true;
                if (node.data.extends?.tag) {
                    data.io.tag = node.data.extends?.tag;
                }
                if (node.data.extends?.desc) {
                    data.io.desc = node.data.extends?.desc
                }
                if (node.data.extends?.category) {
                    data.io.category = node.data.extends?.category
                }
                return
            }
        }
        data.dlg.dataType = 0;
        data.dlg.show = true;
    })
})

//暴露setCurNode方法
defineExpose({
    addNode,
    // setCurNode, //设置当前选择的算子节点
    loadGraph,//加载图形
    saveGraph,//保存图形
});
</script>

<style scoped>
.button-container {
    /* height: 40px; */
    position: fixed;
    top: 0;
    left: 25%;

}

.graph-container {
    /* height: calc(100% - 40px); */
    width: 100%;
    height: 100%;
}

.graph-container>div {
    width: 100%;
    height: 100%;
}

.upload-btn {
    display: inline-block;
    vertical-align: middle;
}

:deep(.el-upload-list) {
    margin: 0;
}

.logo {
    height: 6em;
    padding: 1.5em;
    will-change: filter;
    transition: filter 300ms;
}

.logo:hover {
    filter: drop-shadow(0 0 2em #646cffaa);
}

.logo.vue:hover {
    filter: drop-shadow(0 0 2em #42b883aa);
}

#container {
    display: flex;
    width: 100%;
    height: 100%;
    border: 1px solid #dfe3e8;
}

#stencil {
    width: 200px;
    height: 100%;
    position: relative;
    border-right: 1px solid #dfe3e8;
}

#btn_setting {
    margin: 0 0 0 0;
}

#graph-container {
    margin-top: 0px;
    /* width: calc(100% - 180px); */
    width: 100%;
    /* height: 100%;
    height: calc(100% - 50px); */
}

.x6-widget-stencil {
    background-color: #fff;
}

.x6-widget-stencil-title {
    background-color: #fff;
}

.x6-widget-stencil-group-title {
    background-color: #fff !important;
}

.x6-widget-transform {
    margin: -1px 0 0 -1px;
    padding: 0px;
    border: 1px solid #239edd;
}

.x6-widget-transform>div {
    border: 1px solid #239edd;
}

.x6-widget-transform>div:hover {
    background-color: #3dafe4;
}

.x6-widget-transform-active-handle {
    background-color: #3dafe4;
}

.x6-widget-transform-resize {
    border-radius: 0;
}

.x6-widget-selection-inner {
    border: 1px solid #239edd;
}

.x6-widget-selection-box {
    opacity: 0;
}
</style>
